package com.poizon.infrastructure.common.util;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 自定义/spel表达式解析/spring环境
 *
 * @author maofeiyu
 */
@Slf4j
public class CustomSpelParseUtil {
    private static final String REGEX = "(\\{([^}]*)\\})|([^{}]*)";
    private static final Pattern PATTERN = Pattern.compile(REGEX);
    private static final SpelExpressionParser PARSER = new SpelExpressionParser();
    private static final LocalVariableTableParameterNameDiscoverer LOCAL_VARIABLE_TABLE = new LocalVariableTableParameterNameDiscoverer();

    /**
     * spel格式,普通参数{#参数名称},对象#{对象名.属性名},例如: "test:{#name},{#age},{#testPnk.ma}"
     */
    public static String parseSpelValue(ProceedingJoinPoint joinPoint, String spelExpression) {
        log.debug("parseSpelValue() called with parameters => 【joinPoint = {}】, 【spelExpression = {}】", joinPoint, spelExpression);

        Assert.notNull(joinPoint);
        Assert.notNull(joinPoint.getSignature());
        Assert.notNull(joinPoint.getArgs());
        Assert.notNull(spelExpression);

        StandardEvaluationContext context = buildSpringSpelContext(joinPoint);

        String businessId = parseCustomSpel(spelExpression, context);

        // 校验幂等键是否正常生成
        Assert.notBlank(businessId, "幂等键解析异常");

        if (log.isDebugEnabled()) {
            log.debug(">parseSpelValue() returned: " + businessId);
        }
        return businessId;
    }

    /**
     * 按照{#user.age}格式解析,并从spring spel上下文获取值
     */
    private static String parseCustomSpel(String spelExpression, StandardEvaluationContext context) {
        try {

            StringBuilder otherContent = new StringBuilder();
            Matcher matcher = PATTERN.matcher(spelExpression);
            while (matcher.find()) {
                String groupOne = matcher.group(1);
                if (groupOne != null) {
                    String groupTwo = matcher.group(2);
                    String value = PARSER.parseExpression(groupTwo).getValue(context, String.class);
                    if (StrUtil.isNotBlank(value)) {
                        otherContent.append(value);
                    } else {
                        log.error(StrUtil.format(">spel表达式={},解析存在空值,字段={}", spelExpression, groupOne));
                    }
                } else {
                    String groupThree = matcher.group(3);
                    if (groupThree != null) {
                        otherContent.append(groupThree);
                    }
                }
            }
            return otherContent.toString();
        } catch (Exception e) {
            // 正则表达式匹配取值出现异常
            log.error(StrUtil.format(">SPEL表达式解析失败:{}", e.getMessage()), e);
        }
        return null;
    }

    private static StandardEvaluationContext buildSpringSpelContext(ProceedingJoinPoint joinPoint) {
        Assert.notNull(joinPoint);
        Assert.notNull(joinPoint.getArgs());

        Object[] args = joinPoint.getArgs();
        StandardEvaluationContext context = new StandardEvaluationContext();
        if (args.length != 0) {
            Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
            String[] paraNameArr = LOCAL_VARIABLE_TABLE.getParameterNames(method);

            for (int i = 0; i < paraNameArr.length; i++) {
                context.setVariable(paraNameArr[i], args[i]);
            }
        }
        return context;
    }
}



